<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JSSIP</title>
    <link rel="stylesheet" href="css/style.css"/>

    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="./bootstrap-3.3.7/css/bootstrap.min.css">

    <!-- 可选的 Bootstrap 主题文件（一般不用引入） -->
    <link rel="stylesheet" href="./bootstrap-3.3.7/css/bootstrap-theme.min.css">
</head>
<body>
<div>
    <input id="number" value="18021411953" type="text"/>
    <button id="startCall">Start Call</button>
    <button id="endCall">End Call</button>
    <button id="transfer">transfer</button>
    <button id="hold">hold</button>
    <button id="unhold">unhold</button>
    <button id="mute">mute</button>
    <button id="unmute">unmute</button>
    <button id="unregister">unregister</button>
    <button id="register">register</button>
</div>
<br>
<div>
    <input id="msg" value="你好" type="text"/>
    <button id="sendMessage">Send Message</button>

    <audio id="ringtone" loop src="./sounds/ringtone.wav"></audio>            <!--//来电提示音-->
    <audio id="ringbacktone" loop src="./sounds/ringbacktone.wav"></audio>    <!--//电话呼叫后声音-->
    <audio id="dtmfTone" src="./sounds/dtmf.wav"></audio>

    <video id="remoteVideo" autoplay="autoplay"></video>
    <video id="localVideo" autoplay="autoplay" muted="muted"></video>
    <!-- DTMF -->
    <div id="dtmf-buttons">
        <button onclick="sendDtmf('1');">1</button>
        <button onclick="sendDtmf('2');">2</button>
        <button onclick="sendDtmf('3');">3</button>
        <button onclick="sendDtmf('4');">4</button>
        <button onclick="sendDtmf('5');">5</button>
        <button onclick="sendDtmf('6');">6</button>
        <button onclick="sendDtmf('7');">7</button>
        <button onclick="sendDtmf('8');">8</button>
        <button onclick="sendDtmf('9');">9</button>
        <button onclick="sendDtmf('0');">0</button>
        <button onclick="sendDtmf('#');">#</button>
        <button onclick="sendDtmf('*');">*</button>
    </div>
</div>
<hr>
<div>
    <input id="seat" value="1012" type="text" placeholder="请输入"/>
    <button id="seatCall">seatCall</button>
</div>


<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-body">
                <h2 class="incoming"></h2>
            </div>
            <div class="modal-footer" style="text-align: center">
                <button type="button" id="acceptCall" class="btn btn-primary" data-dismiss="modal">接听</button>
                <button type="button" id="rejectCall" class="btn btn-default" data-dismiss="modal">拒绝</button>
            </div>
        </div>
    </div>
</div>


<script src="./js/jquery-3.4.1.min.js"></script>
<script src="./js/audioPlayer.js"></script>

<script src="./bootstrap-3.3.7/js/bootstrap.min.js"></script>

<script src="./js/jssip-3.3.11.js"></script>
<script>JsSIP.debug.enable('JsSIP:*');</script>

<script type="text/javascript">
    var Ring = {
        /**
         * 振铃
         */
        startRingTone: function () {
            let play = document.getElementById("ringtone").play();
            play.then(() => {

            }).catch((err) => {

            });

        },
        /**
         * 停止振铃
         */
        stopRingTone: function () {
            document.getElementById("ringtone").pause();
        },

        startRingbackTone: function () {
            let play = document.getElementById("ringbacktone").play();
            play.then(() => {

            }).catch((err) => {


            });
        },
        stopRingbackTone: function () {
            document.getElementById("ringbacktone").pause();
        }

    };
</script>

<script>

    var CurrentSession = {
        canHold: false,
        ringing: false,
        session: null,
        originator: null
    };

    // 外呼session
    var outgoingSession = null;

    // 来电session
    var incomingSession = null;


    var startCall = document.getElementById('startCall');
    var endCall = document.getElementById('endCall');
    var transfer = document.getElementById('transfer');
    var hold = document.getElementById('hold');
    var unhold = document.getElementById('unhold');
    var sendMessage = document.getElementById('sendMessage');
    var mute = document.getElementById('mute');
    var unmute = document.getElementById('unmute');
    var remoteVideo = document.getElementById('remoteVideo');
    var localVideo = document.getElementById('localVideo');
    var options = {
        // 'eventHandlers'    : eventHandlers,
        'mediaConstraints': {'audio': true, 'video': false},
        'extraHeaders': [],
        'sessionTimersExpires': 180
    };
    var localStream = null;


    // Create our JsSIP instance and run it:
    var socket = new JsSIP.WebSocketInterface('wss://192.168.0.254:7443');
    var configuration = {
        sockets: [socket],
        uri: 'sip:17849982282@192.168.0.254:5066',
        authorization_user: '17849982282',
        password: '17849982282',
        // WebSocket重新连接调度之间的最大间隔(秒)。默认值是30。
        connection_recovery_max_interval: 60,
        // WebSocket重新连接尝试之间的最小间隔(秒)。默认值是2。
        connection_recovery_min_interval: 4,
        // String 注册到fs显示的url
        contact_uri: 'sip:17849982282@192.168.0.254:5066;transport=wss',
        // 外显
        display_name: '10086',
        // 来电后,120秒内未接听,将会拒绝.默认60s
        no_answer_timeout: 120,
        // 会话计时器,默认true
        session_timers: false,
        // 用于刷新会话计时器的SIP方法。有效值是UPDATE或INVITE。默认值是UPDATE。
        session_timers_refresh_method: 'invite',
        // 是否应该在启动时自动注册.默认true
        register: true,
        // 注册过期时间(秒) 默认值是600。
        register_expires: 300,
        //  User-Agent header中的值,默认是JsSIP
        user_agent: "bang",
        registrar_server: "sip:192.168.0.254:5066"
    };

    var ua = new JsSIP.UA(configuration);

    // 连接到信令服务器，并在以前停止时恢复以前的状态。对于新的开始，如果UA配置中的register参数设置为true，则使用SIP域注册。
    ua.start();
    // 保存当前注册状态，并在优雅地取消注册和终止活动会话(如果有活动会话)之后断开与信令服务器的连接。
    // ua.stop();

    // 注册到服务器 如果设置了register:true,则会自动注册,不需要调用此方法
    // ua.register();

    // 取消注册
    // ua.unregister({
    //     all: true
    // });

    if (ua.isRegistered()) {
        alert("注册成功");
    } else {
        ua.register();
    }

    if (ua.isConnected()) {
        alert("the transport is connected");
    }

    ua.on('connecting', function (data) {
        console.error("connecting 正在建立连接 .........");
        let dataSocket = data.socket;
        let attempts = data.attempts;
        console.log(dataSocket);
        console.log(attempts);
        console.error("connecting 正在建立连接 .........");
    });

    ua.on('connected', function (data) {
        console.error("connected 已经建立连接 .........");
        let dataSocket = data.socket;
        console.log(dataSocket);
        console.error("connected 已经建立连接 .........");
    });

    ua.on('disconnected', function (data) {
        console.error("disconnected 断开连接 .........");
        let dataSocket = data.socket;
        let error = data.error;
        let code = data.code;
        let reason = data.reason;
        console.log(data);
        console.error("disconnected 断开连接 .........");
    });

    ua.on('registered', function (data) {
        console.error("registered 注册成功 .........");
        let response = data.response;
        console.log(response);
        console.error("registered 注册成功 .........");

    });

    ua.on('unregistered', function (data) {
        console.error("unregistered 取消注册 .........");
        let response = data.response;
        let cause = data.cause;
        console.log(data);
        console.error("unregistered 取消注册 .........");
    });

    ua.on('registrationFailed', function (data) {
        console.error("registrationFailed 注册失败 .........");
        let response = data.response;
        let cause = data.cause;
        console.log(data);
        console.error("registrationFailed 注册失败 .........");
    });


    var globalSession = null;

    // 新来会话
    ua.on('newRTCSession', function (data) {

        console.log('新来会话 onNewRTCSession: ', data);

        //  remote: 新会话由远程对等点生成。
        //  local: 新会话由本地用户生成。
        let originator = data.originator;
        // session实例
        let session = data.session;
        //  remote:JsSIP.IncomingRequest实例. 接收到的INVITE请求的IncomingRequest实例。
        //  local: JsSIP.OutgoingRequest实例. 发出邀请请求的OutgoingRequest实例。
        let request = data.request;
        globalSession = session;
        if (CurrentSession.session != null&&CurrentSession.session.direction==='incoming') {
            session.terminate({
                    'status_code'   : 486,
                    'reason_phrase' : 'Busy Here'
                });
            return;
        }
        // 赋值
        CurrentSession.session = session;
        CurrentSession.originator = originator;



        // 注册事件
        // session.on('peerconnection', function (data) {
        //     let peerconnection1 = data.peerconnection;
        //     console.error('onPeerconnection - ', peerconnection1);
        //     console.error('CurrentSession.session - ', CurrentSession.session);
        //
        //
        //     // Handle local stream
        //     if (peerconnection1.getLocalStreams()[0]){
        //         // Clone local stream
        //         // Display local stream
        //         localVideo.srcObject =peerconnection1.getLocalStreams()[0].clone();
        //     }
        //
        //     peerconnection1.onaddstream = function (ev) {
        //         console.error('onaddstream from remote - ', ev);
        //         handleRemoteStream(event.stream);
        //     };
        //
        // });

        session.on('accepted', function (data) {
            Ring.stopRingTone();

            console.error('onAccepted - ', data);
            console.error('CurrentSession.session - ', CurrentSession.session);

            CurrentSession.ringing = false;
            CurrentSession.canHold = true;
            CurrentSession.session = this;

            if (data.originator == 'remote') {

                console.error("accepted setCurrentSession - ", CurrentSession.session);
                incomingSession = null;

            }else{
                let connection = CurrentSession.session.connection;
                console.error("accepted setCurrentSession - connection", connection);

                localStream = connection.getLocalStreams()[0];
                // Handle local stream
                if (localStream) {
                    // Clone local stream
                    // Display local stream
                    localVideo.srcObject = localStream.clone();
                    localVideo.volume = 1;
                }
                console.error("localStream", connection.getLocalStreams());


                const remoteStream = connection.getRemoteStreams()[0];
                // If incoming all we already have the remote stream
                if (remoteStream) {
                    handleRemoteStream(remoteStream);
                }

                connection.addEventListener('addstream', (event) => {
                    console.info('peerconnection "addstream" event');
                    handleRemoteStream(event.stream);
                });
            }
        });


        session.on('confirmed', function (data) {
            console.error('onConfirmed - ', data);
            console.error('CurrentSession.session - ', CurrentSession.session);
            CurrentSession.session = this;
            if (data.originator == 'remote') {
                console.error("setCurrentSession - ",  CurrentSession.session );
                incomingSession = null;
            }

        });


        session.on('progress', function (data) {
            console.error('CurrentSession.session - ', CurrentSession.session);
            CurrentSession.ringing = true;

            console.error('onProgress - ', data.originator);
            if (data.originator == 'remote') {
                console.error('onProgress, response - ', data.response);
            }
        });


        session.on('ended', function (data) {
            Ring.stopRingTone();
            console.error('CurrentSession.session - ', CurrentSession.session);
            CurrentSession.ringing = false;
            CurrentSession.canHold = false;
            CurrentSession.session = null;
            CurrentSession.originator = null;


            let originator = data.originator;
            let message = data.message;
            let cause = data.cause;

            console.error('ended -originator ', originator);
            console.error('ended, message - ', message);
            console.error('ended, cause - ', cause);


        });


        session.on('failed', function (data) {
            Ring.stopRingTone();
            console.error('CurrentSession.session - ', CurrentSession.session);
            CurrentSession.ringing = false;
            CurrentSession.canHold = false;
            CurrentSession.session = null;
            CurrentSession.originator = null;


            let originator = data.originator;
            let message = data.message;
            let cause = data.cause;
            console.error('failed -originator ', originator);
            console.error('failed, message - ', message);
            console.error('failed, cause - ', cause);
        });


         // 处理session
        if (originator == 'remote') {
            //incoming call
            console.error("incomingSession, answer the call");
            Ring.startRingTone(); //来电振铃
            let url = session.remote_identity.uri.toString() + "来电了，是否接听";
            let toString = session.remote_identity.display_name.toString();

            let callsource = request.getHeader('X-Call_Source');
            let flowdata = request.getHeader('X-Flowdata');
            let recordFile = request.getHeader('X-Record_File');
            $(".incoming").html(url);
            $("#myModal").modal('show');
            incomingSession = session;

        } else {
            // outbound call 外呼
            console.error("outgoingSession");
            outgoingSession = session;
            outgoingSession.on('connecting', function (data) {
                console.error('onConnecting - ', data.request);
                CurrentSession.session = outgoingSession;
                outgoingSession = null;
            });
        }


        let peerconnection = session.connection;
        if (peerconnection != null) {
            const localStream = peerconnection.getLocalStreams()[0];
            const remoteStream = peerconnection.getRemoteStreams()[0];
            // Handle local stream
            if (localStream) {
                // Clone local stream
                // Display local stream
                localVideo.srcObject = localStream.clone();
                localVideo.volume = 1;

            }
            // If incoming all we already have the remote stream
            if (remoteStream) {
                handleRemoteStream(remoteStream);
            }
            peerconnection.addEventListener('addstream', (event) => {
                console.info('peerconnection "addstream" event');
                handleRemoteStream(event.stream);
            });
        }


    });


    // 新的sip消息
    ua.on('newMessage', function (data) {
        console.error("newMessage 新的sip消息 .........");
        //  remote: 新会话由远程对等点生成。
        //  local: 新会话由本地用户生成。
        let originator = data.originator;
        // JsSIP.Message 实例
        let message = data.message;
        //  remote:JsSIP.IncomingRequest实例. 接收到的MESSAGE请求的IncomingRequest实例。
        //  local: JsSIP.OutgoingRequest实例. 发出MESSAGE请求的OutgoingRequest实例。
        let request = data.request;

        if (originator === 'remote') {
            // 接收消息
            message.accept();
        }
        console.error(data);
        console.error("newMessage 新的sip消息 .........");
    });

    // sipEvent
    ua.on('sipEvent', function (data) {
        console.error("sipEvent 新的sipEvent .........");
        let event = data.event;
        // 接收到的通知请求的IncomingRequest实例。
        let request = data.request;

        console.error(data);
        console.error("sipEvent 新的sipEvent .........");
    });


    /**
     *  绑定方法
     * @param obj 要绑定事件的元素
     * @param ev 绑定的事件
     * @param fn  绑定事件的函数
     */
    function bindEvent(obj, ev, fn) {
        if (obj.attachEvent) {
            obj.attachEvent("on" + ev, fn);
        }
        else {
            obj.addEventListener(ev, fn, false);
        }
    }

    /**
     *  发送DTMF
     * @param num 按键
     */
    function sendDtmf(num) {
        if (CurrentSession.session==null) {
            alert("未建立会话,无法发送DTMF");
            return;
        }
        let options = {
            'duration': 160,
            'interToneGap': 1200,
            'extraHeaders': ['X-Foo: foo', 'X-Bar: bar']
        };
        CurrentSession.session.dtmf(num, options);
    }

    /**
     * 拨打电话
     */
    bindEvent(startCall, 'click', function () {

        var number = document.getElementById("number").value;
        let extraHeaders = [];
        let contact = "sofia/internal/%s@192.168.0.250:5117";
        let replacedContact = contact.replace("%s", number);
        extraHeaders.push("X-ReContact:" + replacedContact);

        options.extraHeaders = extraHeaders;
        //创建一个新的出站（用户代理客户端）会话
        number = "rel" + number;
        ua.call(number, options);
    });


    var seatCall = document.getElementById('seatCall');


    // 坐席之间呼叫
    bindEvent(seatCall, 'click', function () {
        let number = document.getElementById("seat").value;
        //创建一个新的出站（用户代理客户端）会话
        number = "ttest" + number;
        ua.call(number, options);
    });


    //挂断事件
    bindEvent(endCall, 'click', function () {
        let options = {
            extraHeaders: ["", "", ""], // Array of Strings with extra SIP headers for the MESSAGE request.
            status_code: "600", //  Number between 300 and 699 representing the SIP response code.
            reason_phrase: "bye", // 表示SIP原因短语的字符串。
            body: "body", //表示SIP消息体的字符串(如果设置了此参数，则必须在extraHeader字段中设置相应的Content-Type头字段)。
        };
        // ua.terminateSessions(options);
        ua.terminateSessions();
    });


    //电话转移盲转
    bindEvent(transfer, 'click', function () {
        if (!CurrentSession.session) {
            return;
        }
        let number = document.getElementById("number").value;
        CurrentSession.session.refer(number, {
            eventHandlers: {
                requestSucceeded: function (data) {
                    console.log("refer --------> requestSucceeded response", data.response);
                },
                requestFailed: function (data) {
                    console.log("refer --------> requestFailed response", data.response, "cause", data.cause);
                },
                trying: function (data) {
                    console.log("refer --------> trying request", data.request, "status_line", data.status_line);
                },
                failed: function (data) {
                    console.log("refer --------> failed request", data.request, "status_line", data.status_line);
                },
                progress: function (data) {
                    console.log("refer --------> progress request", data.request, "status_line", data.status_line);
                },
                accepted: function (data) {
                    console.log("refer --------> accepted request", data.request, "status_line", data.status_line);
                }
            }
        });
    });


    //电话保持
    bindEvent(hold, 'click', function () {
        if (!CurrentSession.session) {
            return;
        }
        if (CurrentSession.canHold) {
            CurrentSession.session.hold({useUpdate: false});
        }
    });


    //恢复电话保持
    bindEvent(unhold, 'click', function () {
        if (!CurrentSession.session) {
            return;
        }
        if (CurrentSession.canHold) {
            CurrentSession.session.unhold({useUpdate: false});
        }

    });

    //发送消息
    bindEvent(sendMessage, 'click', function () {
        // Sends a new message
        let eventHandlers = {
            'succeeded': function (data) {
                console.error("succeeded send msg ");
                console.error(data);
            },
            'failed': function (data) {
                console.error("failed send msg ");
                console.error(data);
            }
        };

        let options = {
            'eventHandlers': eventHandlers
        };
        let message = sendMessage.innerText;

        ua.sendMessage('sip:1012@192.168.0.108:50583', message, options);

    });

    // 静音
    bindEvent(mute, 'click', function () {
        if (!CurrentSession.session) {
            console.warn("No session to toggle mute");
            return;
        }
        CurrentSession.session.mute({
            audio: true,
            video: true
        });

    });

    // 解除静音
    bindEvent(unmute, 'click', function () {
        if (!CurrentSession.session) {
            console.warn("No session to toggle mute");
            return;
        }
        CurrentSession.session.unmute({
            audio: true,
            video: true
        });

    });

    // 接听电话
    bindEvent(document.getElementById('acceptCall'), 'click', function () {
        if (null == CurrentSession.session) {
            return;
        }
        if (CurrentSession.originator=='remote') {
            CurrentSession.session.answer({
                'mediaConstraints': {
                    'audio': true,
                    'video': false,
                },
                'mediaStream': localStream
            });
        }

    });

    // 拒绝电话
    bindEvent(document.getElementById('rejectCall'), 'click', function () {
        if (null == CurrentSession.session) {
            return;
        }
        if (CurrentSession.originator=='remote') {
            // 拒绝来电
            CurrentSession.session.terminate();
        }
    });


    bindEvent(document.getElementById('unregister'), 'click', function () {
        ua.unregister({all: true});

    });

    bindEvent(document.getElementById('register'), 'click', function () {
        ua.register();

    });


    /**
     *  JSSIP处理远程媒体
     * @param stream
     */
    function handleRemoteStream(stream) {
        // Display remote stream
        remoteVideo.srcObject = stream;

        stream.addEventListener('addtrack', (event) => {
            const track = event.track;

            if (remoteVideo.srcObject !== stream)
                return;


            // Refresh remote video
            remoteVideo.srcObject = stream;
            track.addEventListener('ended', () => {

            });
        });

        stream.addEventListener('removetrack', () => {
            if (remoteVideo.srcObject !== stream)
                return;
            // Refresh remote video
            remoteVideo.srcObject = stream;

        });
    }

    // 监控页面刷新
    window.onbeforeunload = function (event) {
        event.returnValue = "页面刷新或关闭...";
        return "页面刷新或关闭";
    };

    // 页面关闭时挂掉所有电话
    window.onunload = function () {
        if (ua != null) {
            console.error("关闭session");
            localStorage.setItem("store", "执行关闭会话 ua");
            ua.terminateSessions();
        } else if (CurrentSession.session != null) {
            console.error("关闭会话");
            localStorage.setItem("store", "执行关闭会话 CurrentSession.session");
            CurrentSession.session.terminate();
        }
    };

</script>

</body>
</html>